package ${packagePrefix}.application.service;

import ${packagePrefix}.application.assembler.${domainObjectName}Assembler;
import ${packagePrefix}.application.service.impl.${domainObjectName}ServiceImpl;
import ${packagePrefix}.client.dto.command.${domainObjectName}AddCmd;
import ${packagePrefix}.client.dto.command.${domainObjectName}ModifyCmd;
import ${packagePrefix}.client.dto.data.${domainObjectName}DTO;
import ${packagePrefix}.client.dto.query.${domainObjectName}GetQry;
import ${packagePrefix}.client.dto.query.${domainObjectName}ListQry;
import ${packagePrefix}.domain.gateway.${domainObjectName}Gateway;
import ${packagePrefix}.domain.model.${tableName?lower_case}.${domainObjectName};
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

class ${domainObjectName}ServiceTest {

    @InjectMocks
    private ${domainObjectName}ServiceImpl ${domainObjectName?uncap_first}Service;

    @Mock
    private ${domainObjectName}Gateway ${domainObjectName?uncap_first}Gateway;

    @Mock
    private ${domainObjectName}Assembler ${domainObjectName?uncap_first}Assembler;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void shouldAddSuccessfully() {
        // Given
        ${domainObjectName}AddCmd cmd = new ${domainObjectName}AddCmd();
        ${domainObjectName} entity = new ${domainObjectName}();
        
        when(${domainObjectName?uncap_first}Assembler.toEntity(any(${domainObjectName}AddCmd.class))).thenReturn(entity);
        when(${domainObjectName?uncap_first}Gateway.save(any(${domainObjectName}.class))).thenReturn(1L);
        
        // When
        Long result = ${domainObjectName?uncap_first}Service.add(cmd);
        
        // Then
        assertEquals(1L, result);
        verify(${domainObjectName?uncap_first}Assembler).toEntity(cmd);
        verify(${domainObjectName?uncap_first}Gateway).save(entity);
    }

    @Test
    void shouldModifySuccessfully() {
        // Given
        ${domainObjectName}ModifyCmd cmd = new ${domainObjectName}ModifyCmd();
        cmd.setId(1L);
        
        ${domainObjectName} entity = new ${domainObjectName}();
        
        when(${domainObjectName?uncap_first}Gateway.findById(1L)).thenReturn(Optional.of(entity));
        
        // When
        ${domainObjectName?uncap_first}Service.modify(cmd);
        
        // Then
        verify(${domainObjectName?uncap_first}Gateway).findById(1L);
        verify(${domainObjectName?uncap_first}Assembler).updateEntity(entity, cmd);
        verify(${domainObjectName?uncap_first}Gateway).save(entity);
    }

    @Test
    void shouldThrowExceptionWhenModifyNonExistentEntity() {
        // Given
        ${domainObjectName}ModifyCmd cmd = new ${domainObjectName}ModifyCmd();
        cmd.setId(1L);
        
        when(${domainObjectName?uncap_first}Gateway.findById(1L)).thenReturn(Optional.empty());
        
        // When & Then
        assertThrows(RuntimeException.class, () -> ${domainObjectName?uncap_first}Service.modify(cmd));
        verify(${domainObjectName?uncap_first}Gateway).findById(1L);
        verify(${domainObjectName?uncap_first}Assembler, never()).updateEntity(any(), any());
        verify(${domainObjectName?uncap_first}Gateway, never()).save(any());
    }

    @Test
    void shouldGetByIdSuccessfully() {
        // Given
        ${domainObjectName}GetQry qry = new ${domainObjectName}GetQry();
        qry.setId(1L);
        
        ${domainObjectName} entity = new ${domainObjectName}();
        ${domainObjectName}DTO dto = new ${domainObjectName}DTO();
        
        when(${domainObjectName?uncap_first}Gateway.findById(1L)).thenReturn(Optional.of(entity));
        when(${domainObjectName?uncap_first}Assembler.toDTO(entity)).thenReturn(dto);
        
        // When
        ${domainObjectName}DTO result = ${domainObjectName?uncap_first}Service.getById(qry);
        
        // Then
        assertNotNull(result);
        assertEquals(dto, result);
        verify(${domainObjectName?uncap_first}Gateway).findById(1L);
        verify(${domainObjectName?uncap_first}Assembler).toDTO(entity);
    }

    @Test
    void shouldReturnNullWhenGetByIdNotFound() {
        // Given
        ${domainObjectName}GetQry qry = new ${domainObjectName}GetQry();
        qry.setId(1L);
        
        when(${domainObjectName?uncap_first}Gateway.findById(1L)).thenReturn(Optional.empty());
        
        // When
        ${domainObjectName}DTO result = ${domainObjectName?uncap_first}Service.getById(qry);
        
        // Then
        assertNull(result);
        verify(${domainObjectName?uncap_first}Gateway).findById(1L);
        verify(${domainObjectName?uncap_first}Assembler, never()).toDTO(any());
    }

    @Test
    void shouldListSuccessfully() {
        // Given
        ${domainObjectName}ListQry qry = new ${domainObjectName}ListQry();
        
        ${domainObjectName} entity = new ${domainObjectName}();
        List<${domainObjectName}> entities = Collections.singletonList(entity);
        
        ${domainObjectName}DTO dto = new ${domainObjectName}DTO();
        
        when(${domainObjectName?uncap_first}Gateway.findByCondition(qry)).thenReturn(entities);
        when(${domainObjectName?uncap_first}Assembler.toDTO(entity)).thenReturn(dto);
        
        // When
        List<${domainObjectName}DTO> result = ${domainObjectName?uncap_first}Service.list(qry);
        
        // Then
        assertNotNull(result);
        assertEquals(1, result.size());
        assertEquals(dto, result.get(0));
        verify(${domainObjectName?uncap_first}Gateway).findByCondition(qry);
        verify(${domainObjectName?uncap_first}Assembler).toDTO(entity);
    }
} 